package com.yawn.zk.client;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.transaction.CuratorTransaction;
import org.apache.curator.framework.recipes.cache.NodeCache;
import org.apache.curator.framework.recipes.cache.NodeCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;

import java.util.Collections;
import java.util.List;

public class ZkClient {
    /**
     * 客户端
     */
    private CuratorFramework curator;

    /**
     * connectionString
     * 服务器列表，格式host1:port1,host2:port2,...
     * retryPolicy
     * 重试策略,内建有四种重试策略,也可以自行实现RetryPolicy接口
     * sessionTimeoutMs
     * 会话超时时间，单位毫秒，默认60000ms
     * connectionTimeoutMs
     * 连接创建超时时间，单位毫秒，默认60000ms
     *
     * @param connectionInfo zk主机
     */
    public ZkClient(String connectionInfo) {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        curator = CuratorFrameworkFactory.newClient(connectionInfo, retryPolicy);
        curator.start();
    }

    /**
     * 获取子节点
     */
    public List<String> getChildren(String path) {
        try {
            return curator.getChildren().forPath(path);
        } catch (Exception e) {
            e.printStackTrace();
            return Collections.emptyList();
        }
    }


    /**
     * 创建节点
     *
     * @param path       路径
     * @param createMode 节点类型
     * @param data       节点数据
     * @return 是否创建成功
     */
    public boolean createNode(String path, CreateMode createMode, String data) {
        try {
            curator.create()
                    .creatingParentsIfNeeded()
                    .withMode(createMode)
                    .forPath(path, data == null? null : data.getBytes());
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 删除节点
     *
     * @param path 路径
     * @return 删除结果
     */
    public boolean deleteNode(String path) {
        try {
            curator.delete().forPath(path);
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 删除一个节点，并且递归删除其所有的子节点
     *
     * @param path 路径
     * @return 删除结果
     */
    public boolean deleteChildrenIfNeededNode(String path) {
        try {
            curator.delete().deletingChildrenIfNeeded().forPath(path);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 判断节点是否存在
     *
     * @param path 路径
     * @return true-存在  false-不存在
     */
    public boolean isExistNode(String path) {
        try {
            Stat stat = curator.checkExists().forPath(path);
            return stat != null;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 判断节点是否是持久化节点
     *
     * @param path 路径
     * @return 2-节点不存在  | 1-是持久化 | 0-临时节点
     *  <br/>
     * Stat信息
     * <table border="0" align="left">
     * <tbody>
     * <tr>
     * <td align="center"><strong>状态属性</strong></td>
     * <td align="center"><strong>说明</strong></td>
     * </tr>
     * <tr>
     * <td>cZxid</td>
     * <td>数据节点创建时的事务ID</td>
     * </tr>
     * <tr>
     * <td>ctime</td>
     * <td>数据节点创建时的时间</td>
     * </tr>
     * <tr>
     * <td>mZxid</td>
     * <td>数据节点最后一次更新时的事务ID</td>
     * </tr>
     * <tr>
     * <td>mtime</td>
     * <td>数据节点最后一次更新时的时间</td>
     * </tr>
     * <tr>
     * <td>pZxid</td>
     * <td>数据节点的子节点列表最后一次被修改（是子节点列表变更，而不是子节点内容变更）时的事务ID</td>
     * </tr>
     * <tr>
     * <td>cversion</td>
     * <td>子节点的版本号</td>
     * </tr>
     * <tr>
     * <td>dataVersion</td>
     * <td>数据节点的版本号</td>
     * </tr>
     * <tr>
     * <td>aclVersion</td>
     * <td>数据节点的ACL版本号</td>
     * </tr>
     * <tr>
     * <td>ephemeralOwner</td>
     * <td>如果节点是临时节点，则表示创建该节点的会话的SessionID；如果节点是持久节点，则该属性值为0</td>
     * </tr>
     * <tr>
     * <td>dataLength</td>
     * <td>数据内容的长度</td>
     * </tr>
     * <tr>
     * <td>numChildren</td>
     * <td>数据节点当前的子节点个数</td>
     * </tr>
     * </tbody>
     * </table>
     */
    public int isPersistentNode(String path) {
        try {
            Stat stat = curator.checkExists().forPath(path);
            if (stat == null) {
                return 2;
            }
            if (stat.getEphemeralOwner() > 0) {
                return 1;
            }
            return 0;
        } catch (Exception e) {
            e.printStackTrace();
            return 2;
        }

    }

    /**
     * 获取节点数据
     *
     * @param path 路径
     * @return 节点数据，如果出现异常，返回null
     */
    public String getNodeData(String path) {
        try {
            byte[] bytes = curator.getData().forPath(path);
            return new String(bytes);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 注册节点数据变化事件
     *
     * @param path              节点路径
     * @param nodeCacheListener 监听事件
     * @return 注册结果
     */
    public boolean registerWatcherNodeChanged(String path, NodeCacheListener nodeCacheListener) {
        NodeCache nodeCache = new NodeCache(curator, path, false);
        try {
            nodeCache.getListenable().addListener(nodeCacheListener);
            nodeCache.start(true);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }


    /**
     * 更新节点数据
     *
     * @param path     路径
     * @param newValue 新的值
     * @return 更新结果
     */
    public boolean updateNodeData(String path, String newValue) {
        //判断节点是否存在
        if (!isExistNode(path)) {
            return false;
        }
        try {
            curator.setData().forPath(path, newValue.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 开启事务
     *
     * @return 返回当前事务
     */
    public CuratorTransaction startTransaction() {
        return curator.inTransaction();
    }
}
